// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#ifndef COROUTINE
#define COROUTINE

#ifdef _MSC_VER
#ifdef Yield
#undef Yield
#endif
#endif

#include <stddef.h>

namespace LLVMJit
{

// Abstract class that represents a cooperative thread of execution.

// Allocating a coroutine actually allocates two. Coroutines work as two
//	endpoints, each connected to the other. When a coroutine calls `Yield`, it
//	resumes the execution of the other endpoint where it called `Yield` itself.

// Coroutines are useful because they allow seamless inversion of control.
//	Dolphin itself want to have control over the program flow; that is, it calls
//	functions and methods, and expect those to return often so other events can
//	be processed. Because that Dolphin expects to be in control, the JIT would
//	normally have to emit code in a way that causes it to return often.

// The main problem is that GameCube/Wii games themselves expect to be in
//	control, too. That is, they believe they have all the CPU for themselves,
//	and as such don't think they're going to be interrupted for no reason. This
//	makes it hard to generate code in such a way that will let the emulator
//	retain control.

// Using coroutines allow both parties to feel they're in charge, because
//	control is inverted through a function call instead of through flow control
//	statements (like 'return').

// It is necessary to call `SetEntryPoint` on the allocated coroutine before
//	using it. This method determines the initial function to call from the
//	new execution context.

// The correct implementation to use is platform-dependant. Under Windows, the
//	right class is FiberCoroutine. Under POSIX systems, it is UContextCoroutine.
//	For more details, consult the appropriate header files.

// The Create method creates a coroutine suited for the current platform. It
//	relies on #ifdef'es and the COROUTINE_CLASS #define in the correct headers.
class Coroutine
{
public:
	static Coroutine* Create(size_t stackSize = 0x8000);
	
	typedef void (*EntryPoint)(void*);
	
	virtual ~Coroutine() { }
	virtual void Yield() = 0;
	virtual void SetEntryPoint(EntryPoint entryPoint, void* argument) = 0;
};

}

#endif